/**
 * Copyright  2011 Nokia Corporation.
 *
 */


#include <QApplication>
#include <QDebug>
#include <QMouseEvent>
#include "QtTileGameEngine.h"
#include "GLES2SpriteBatch.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>


const char *audioSamples[ AUDIO_SAMPLE_COUNT ] = {
    "click.wav",
    "change.wav",                   // milton
    "change_completed.wav",         // milton
    "destroy.wav",                      // milton
    "fanfare.wav",                  // by milton
    "game_over.wav",                    // by milton
    "illegal_move.wav",                 // milton
    "next_level.wav",               // by milton @ freesound
    "exp_bonus.wav",                 // by mattwasser
    "xbonus.wav",                 // by Christianjinnyzoe
};


#define AUDIO_SAMPLE_CLICK 0
#define AUDIO_SAMPLE_CHANGE 1
#define AUDIO_SAMPLE_CHANGE_COMPLETED 2
#define AUDIO_SAMPLE_DESTROY 3
#define AUDIO_LEVEL_COMPLETED 4
#define AUDIO_GAME_OVER 5
#define AUDIO_ILLEGAL_MOVE 6
#define AUDIO_NEXT_LEVEL 7
#define AUDIO_SAMPLE_EXP_BONUS 8
#define AUDIO_SAMPLE_X_BONUS 9

/**
 *
 * Custom projection matrix used for rendering our virtual
 * 65536 x 65536 * aspectRatio - screen into our actual resolution
 *
 */
float matrixProjection[16]=
{
    #ifdef Q_WS_HARMATTAN
    0, 2/65536.0f, 0, 0,
    2/(1.5f*65536), 0, 0, 0,
    0, 0, 1, 0,
    -1, -1, 0, 1,
    #else
    2/65536.0f, 0, 0, -1,
    0, -2/(1.5f*65536.0f), 0, 1,
    0, 0, 1, 0,
    0, 0, 0, 1,
    #endif
};


QtTileGameEngine::QtTileGameEngine( QWidget *parent ) : GE::GameWindow( parent )
{
    theGame = 0;
    spriteBatch = 0;
}


QtTileGameEngine::~QtTileGameEngine()
{
    if (theGame) delete theGame;
    if (spriteBatch) delete spriteBatch;

}

/**
 *
 * Update the ITileGame
 *
 */
void QtTileGameEngine::update(const float fFrameDelta )
{
    float ft = fFrameDelta;
    if (ft>0.3f) ft = 0.3f;
    if (ft<0.00001f) ft = 0.00001f;
    if (theGame)
        theGame->run( (int)(ft*65536.0f) );
}

/**
 *
 * Render the ITileGame
 *
 */
void QtTileGameEngine::onRender()
{
    if (theGame)
        theGame->draw();
}


/**
 *
 * Create the system. OpenGL is initialized when arriving here.
 *
 *
 */
int QtTileGameEngine::onCreate() {
    //previousMode=-1;

    // load textures
    for(int i=0;i<TexEndOfList;i++) {
        load((TextureID)i);
    }
    //buyMoreHack=0;

    if (spriteBatch) delete spriteBatch;
    spriteBatch = new GLES2SpriteBatch(100,100);

    // load samples
    for (int f=0; f<AUDIO_SAMPLE_COUNT; f++) {
        QString str = ":/" + QString(audioSamples[f]);
        m_audioBuffers[f] = GE::CAudioBuffer::loadWav( str );
        qDebug() << "Loading audio sample (" << str << "): " << m_audioBuffers[f];
    };

    theGame = createTileGame( this,-2500,0,65536+5000, 65536*3/2+2500 );
    getMixer().setAbsoluteVolume(0.5f);
    return 1;
}



/**
 *
 * Destroy the system. OpenGL is about to be destroyed when entering here
 *
 */
void QtTileGameEngine::onDestroy()
{

    if (spriteBatch) delete spriteBatch;
    spriteBatch = 0;

    for (int f=0; f<AUDIO_SAMPLE_COUNT; f++) {
        qDebug() << "Releasing audiosample: " << m_audioBuffers[f];
        if (m_audioBuffers[f]) delete m_audioBuffers[f];
        m_audioBuffers[f] = 0;
    };

    if (theGame) {
        delete theGame;
        theGame = 0;
    };
}


GLuint QtTileGameEngine::loadGLTexture( const char *fn ) {
    QString filename = QString( fn );
    QImage image = QImage( filename );
    if (image.width() < 1 || image.height() < 1 ) return 0;

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLuint* pTexData = new GLuint[image.width() * image.height()];
    GLuint* sdata = (GLuint*)image.bits();
    GLuint* tdata = pTexData;

    for (int y=0; y<image.height(); y++)
        for (int x=0; x<image.width(); x++) {
            //*tdata = *sdata;
            *tdata = ((*sdata&255) << 16) | (((*sdata>>8)&255) << 8)
                    | (((*sdata>>16)&255) << 0) | (((*sdata>>24)&255) << 24);
            sdata++;
            tdata++;
        };

    qDebug() << "texture : " << image.width() << " " << image.height();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST );
    glGenerateMipmap( GL_TEXTURE_2D );
    delete [] pTexData;
    return texture;
};



void QtTileGameEngine::load(TextureID id)
{
    GameTexture *t=&gameTextures[0];
    while(1)
    {
        if(t->id==TexEndOfList) return;
        if(t->id==id)
        {
            glBindTexture(GL_TEXTURE_2D, t->id);
            char s[256];

            sprintf(s, ":/%s.png", t->name);
            t->glId=loadGLTexture(s);
            qDebug() << "Loading texture:" << s << " succes( "<< t->glId << ")";


            if(!t->tilesX) t->tilesX=1;
            if(!t->tilesY) t->tilesY=1;


            for(int i=0;i<16;i++) t->matrix[i]=0;
            t->matrix[0]=1.0f/t->tilesX;
            t->matrix[4*1+1]=1.0f/t->tilesY;
            t->matrix[4*2+2]=1.0f;
            t->matrix[4*3+3]=1.0f;

            return;
        }
        t++;
    }
}



void QtTileGameEngine::mousePressEvent( QMouseEvent *event ) {

#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536-(event->y()<<16) / height();
    y = y*3/2;
    qDebug() << "MousePress" << x << ":" <<y;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif

    if (theGame) theGame->click(x,y, eMOUSEEVENT_BUTTONDOWN );
};

void QtTileGameEngine::mouseReleaseEvent( QMouseEvent *event ) {
#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536-(event->y()<<16) / height();
    y = y*3/2;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif


    if (theGame) theGame->click(x,y, eMOUSEEVENT_BUTTONUP );

};

void QtTileGameEngine::mouseMoveEvent( QMouseEvent *event ) {
#ifdef Q_WS_HARMATTAN
    int y = (event->x()<<16) / width();
    int x = 65536- (event->y()<<16) / height();
    y = y*3/2;
#else
    int x = (event->x()<<16) / width();
    int y = (event->y()<<16) / height();
    y = y*3/2;
#endif


    if (theGame) theGame->click(x,y, eMOUSEEVENT_MOUSEDRAG);
};

void QtTileGameEngine::keyPressEvent( QKeyEvent *event ) {

};

void QtTileGameEngine::onPause() {

    theGame->pause();
};

void QtTileGameEngine::onResume() {
    theGame->resume();
};

void QtTileGameEngine::setSize( int w, int h ) {

    glViewport(0,0, w,h );
    spriteBatch->setTargetSize(w,h);
}




void QtTileGameEngine::effectNotify( eTILEGAME_EFFECT effect, unsigned int arg1, unsigned int arg2 )
{
    switch(effect)
    {

    case eEFFECT_EXIT:
        qApp->exit(0);
        break;

    case eEFFECT_DESTROYING:
    case eEFFECT_DESTROYING_BONUS:
    {
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_SAMPLE_DESTROY ]->playWithMixer( getMixer() );
            if (arg1>0) {
                GE::CAudioBufferPlayInstance*i = m_audioBuffers[ AUDIO_SAMPLE_EXP_BONUS ]->playWithMixer( getMixer() );
                i->setSpeed( 1.0f + (float)arg1/12.0f );
            }

        }
    }
        break;

    case eEFFECT_GAMEOVER:
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_GAME_OVER ]->playWithMixer( getMixer() );
        }
        break;

    case eEFFECT_NEWLEVEL:
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_NEXT_LEVEL ]->playWithMixer( getMixer() );
        }
        break;


    case eEFFECT_LEVELCOMPLETED:
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_LEVEL_COMPLETED ]->playWithMixer( getMixer() );
        }
        break;

    case eEFFECT_XBONUS:
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_SAMPLE_X_BONUS ]->playWithMixer( getMixer() );
        }
        break;

    case eEFFECT_BLOCK_BEGIN_FINISHED:
        if (audioEnabled()) {
            GE::CAudioBufferPlayInstance *i =  m_audioBuffers[ AUDIO_SAMPLE_CHANGE ]->playWithMixer( getMixer() );
            i->setLeftVolume( 0.1f );
            i->setRightVolume( 0.1f );
        }
        break;

    case eEFFECT_BLOCK_VANISH_STARTED:
        if (audioEnabled()) {
            GE::CAudioBufferPlayInstance *i =  m_audioBuffers[ AUDIO_SAMPLE_CHANGE_COMPLETED ]->playWithMixer( getMixer() );
            i->setLeftVolume( 0.1f );
            i->setRightVolume( 0.1f );
        }
        break;

    case eCHANGING:
        if (audioEnabled()) {
            GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_SAMPLE_CHANGE ]->playWithMixer( getMixer() );
            i->setSpeed(0.8 + (float)(rand() & 255) /255.0f * 0.4f );
        }
        break;

    case eCHANGE_COMPLETED:
        if (audioEnabled()) {
            GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_SAMPLE_CHANGE_COMPLETED ]->playWithMixer( getMixer() );
            i->setSpeed(0.8 + (float)(rand() & 255) /255.0f * 0.4f );
        }

        break;

    case eILLEGAL_MOVE:
        if (audioEnabled()) {
            GE::CAudioBufferPlayInstance *i = m_audioBuffers[ AUDIO_ILLEGAL_MOVE ]->playWithMixer( getMixer() );
            i->setSpeed(0.9 + (float)(rand() & 255) /255.0f * 0.2f );
        }
        break;

        /*
                // disabled,... clicks are annoying when scores are changed.
        case eEFFECT_SCORECHANGED:
            if (audioEnabled()) {
                m_audioBuffers[ AUDIO_SAMPLE_CLICK ]->playWithMixer( getMixer() )->setSpeed(4.0f);
            }
            break;
            */


    case eCLICK:
    case eEFFECT_MENU:
        if (audioEnabled()) {
            m_audioBuffers[ AUDIO_SAMPLE_CLICK ]->playWithMixer( getMixer() );
        }
        break;
        /*
        case eEFFECT_BUYMORE:
                //buyMoreHack=1;
                exit(0);
                break;
                */
    default:
        break;
    }
}


void QtTileGameEngine::renderTile( int x, int y,
                                   int w, int h,
                                   int angle,
                                   int mode,
                                   unsigned int tileIndex, unsigned int arg )
{
    GameTexture *gt = gameTextures + ((tileIndex>>16)&0xff) -1;
    SpriteDrawInfo sdi;
    sdi.textureHandle = gt->glId;


    // Change the blending mode if required
    if(mode!=previousMode)
    {
        spriteBatch->end();
        if (mode==0)
            spriteBatch->begin(SpriteBatch::eALPHA, SpriteBatch::eCUSTOMPROJECTION, (float*)matrixProjection );
        else
            spriteBatch->begin(SpriteBatch::eADDITIVE, SpriteBatch::eCUSTOMPROJECTION, (float*)matrixProjection );

        previousMode=mode;
    }


    int t=(tileIndex&65535);
    sdi.setTargetPos( x+(w>>1),y+(h>>1) );
    sdi.angle = (float)angle / 65536.0f * 3.14159f*2.0f;
    sdi.a = 1-((tileIndex>>24)&0xff)/255.0f;
    sdi.setScale(w,-h);

    sdi.setSourceRect((t%gt->tilesX)/(float)gt->tilesX,
                      (t/gt->tilesX%gt->tilesY)/(float)gt->tilesY,
                      1.0f/gt->tilesX,
                      1.0f/gt->tilesY );


    spriteBatch->draw( &sdi );
}


void QtTileGameEngine::renderBackground( unsigned int index )
{
    glClearColor(0.0f, 1.0f, 0.0f, 0.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    previousMode = 0;
    spriteBatch->begin(SpriteBatch::eALPHA, SpriteBatch::eCUSTOMPROJECTION, (float*)matrixProjection );
}


void QtTileGameEngine::renderForeground( unsigned int index )
{
    if (spriteBatch) spriteBatch->end();
}

int QtTileGameEngine::run( int fixedFrameTime16Bit )
{
    return 1;
}




